from time import timezone
from django.db import models
from django.conf import settings
from mptt.models import MPTTModel, TreeForeignKey
from django.db.models.signals import pre_save
from django.dispatch import receiver

# Create your models here.
# 公共信息抽象模型类
class CommonInfo(models.Model):
    # 数据备注（运维人员填写）
    data_remark = models.CharField(verbose_name='数据备注', max_length=200, null=True, blank=True, db_comment='数据备注（运维人员填写）')
    # 创建人
    create_user = models.CharField(verbose_name='创建人', max_length=50, null=True, blank=True, db_comment='创建人')
    # 创建时间（插入数据库时自动生成）
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True, db_comment='创建时间（插入数据库时自动生成）')
    # 更新人
    update_user = models.CharField(verbose_name='更新人', max_length=50, null=True, blank=True, db_comment='更新人')
    # 更新时间（每次update操作后自动更新）
    update_time = models.DateTimeField(verbose_name='更新时间', auto_now=True, db_comment='更新时间（每次update操作后自动更新）')
    # 逻辑删除标记（0：未删除，1：已删除）
    be_deleted = models.IntegerField(verbose_name='逻辑删除标记', choices=((0, '未删除'), (1, '已删除')), default=0, db_comment='逻辑删除标记（0：未删除，1：已删除）')
    # 删除时间
    deleted_time = models.DateTimeField(verbose_name='删除时间', null=True, blank=True, db_comment='删除时间')
    # 是否有效
    valid_status = models.IntegerField('是否有效', choices=((0, '无效'), (1, '有效')), default=1, db_comment='是否有效')

    class Meta:
        abstract = True # 说明是抽象模型类，不会生成迁移文件
    
    # 逻辑删除
    def delete(self, using=None, keep_parents=False):
        self.be_deleted = 1
        self.deleted_time = timezone.now()
        self.save()
    
    # 物理删除
    def hard_delete(self):
        super().delete()



# 字典类型
class DicType(CommonInfo):
    dic_type_id = models.CharField(verbose_name='字典类型ID', max_length=40, null=False, unique=True, blank=False, db_comment='字典类型ID')
    dic_type_code = models.CharField(verbose_name='字典类型编码', max_length=100, null=False, blank=False, db_comment='字典类型编码')
    dic_type_name = models.CharField(verbose_name='字典类型名称', max_length=100, null=False, blank=False, db_comment='字典类型名称')
    description = models.CharField(verbose_name='描述', max_length=200, null=True, blank=True, db_comment='描述')
    remark = models.CharField(verbose_name='备注', max_length=200, null=True, blank=True, db_comment='备注')
    sort_num = models.IntegerField(verbose_name='排序号', null=True, blank=True, db_comment='排序号')
    
    def __str__(self):
        return self.dic_type_name
    
    class Meta:
        db_table = 'dic_type'
        verbose_name = '字典类型'
        verbose_name_plural = verbose_name

# 字典项
class DicItem(CommonInfo, MPTTModel):
    dic_type_id = models.ForeignKey(DicType, to_field='dic_type_id', related_name='dic_items', verbose_name='字典类型', on_delete=models.CASCADE, db_column='dic_type_id', db_comment='字典类型ID')
    dic_item_id = models.CharField(verbose_name='字典项ID', max_length=40, null=False, blank=False, db_comment='字典项ID')
    dic_item_code = models.CharField(verbose_name='字典项编码', max_length=100, null=False, blank=False, db_comment='字典项编码')
    dic_item_name = models.CharField(verbose_name='字典项名称', max_length=100, null=False, blank=False, db_comment='字典项名称')
    description = models.CharField(verbose_name='描述', max_length=200, null=True, blank=True, db_comment='描述')
    remark = models.CharField(verbose_name='备注', max_length=200, null=True, blank=True, db_comment='备注')
    parent_id = TreeForeignKey('self', related_name='children', verbose_name='父字典项主键ID', on_delete=models.CASCADE, null=True, blank=True, db_column='parent_id', db_comment='父字典项主键ID')
    parent_dic_item_id = models.CharField(verbose_name='父字典项ID', max_length=40, null=True, blank=True, db_comment='父字典项ID')
    level_num = models.IntegerField(verbose_name='层级数', null=True, blank=True, db_comment='层级数')
    sort_num = models.IntegerField(verbose_name='排序号', null=True, blank=True, db_comment='排序号')

    def __str__(self):
        return self.dic_item_name

    class MPTTMeta:
        # 指定父节点字段名
        parent_attr = 'parent_id'
        # 指定使用 level_num 作为层级字段
        level_attr = 'level_num'
        # 按排序号和字典项ID排序
        order_insertion_by = ['sort_num', 'dic_item_id'] 
    
    class Meta:
        # 指定数据库表名
        db_table = 'dic_item'
        # 指定模型名称
        verbose_name = '字典项'
        # 指定模型复数名称
        verbose_name_plural = verbose_name
        constraints = [
            # 联合唯一索引，保证字典类型ID和字典项ID的唯一性
            models.UniqueConstraint(fields=['dic_type_id', 'dic_item_id'], name='unique_dic_type_item')
        ]

# 字典项保存前更新父字典项ID
@receiver(pre_save, sender=DicItem)
def update_parent_dic_item_id(sender, instance, **kwargs):
    # 更新父字典项ID
    if instance.parent_id:
        # 如果父节点已存在，直接获取dic_item_id
        if instance.parent_id.pk:
            instance.parent_dic_item_id = instance.parent_id.dic_item_id
        else:
            # 如果父节点是新建的，先保存父节点，再获取dic_item_id
            instance.parent_id.save()
            instance.parent_dic_item_id = instance.parent_id.dic_item_id
    else:
        instance.parent_dic_item_id = None

# 平台
class Platform(CommonInfo):
    platform_id = models.CharField(verbose_name='平台ID', max_length=20, null=False, unique=True, blank=False, db_comment='平台ID')
    platform_code = models.CharField(verbose_name='平台编码', max_length=100, null=False, blank=False, db_comment='平台编码')
    platform_name = models.CharField(verbose_name='平台名称', max_length=100, null=False, blank=False, db_comment='平台名称')
    description = models.CharField(verbose_name='描述', max_length=200, null=True, blank=True, db_comment='描述')
    version = models.CharField(verbose_name='版本', max_length=50, null=True, blank=True, db_comment='版本')
    homepage = models.CharField(verbose_name='主页地址', max_length=200, null=True, blank=True, db_comment='主页地址')
    app_download_url = models.CharField(verbose_name='APP下载地址', max_length=200, null=True, blank=True, db_comment='APP下载地址')
    remark = models.CharField(verbose_name='备注', max_length=200, null=True, blank=True, db_comment='备注')
    sort_num = models.IntegerField(verbose_name='排序号', null=True, blank=True, db_comment='排序号')

    def __str__(self):
        return f'{self.platform_name}({self.platform_code})'
    
    class Meta:
        db_table = 'platform'
        verbose_name = '平台'
        verbose_name_plural = verbose_name

# 菜单
class Menu(CommonInfo, MPTTModel):
    name = models.CharField(verbose_name='菜单名称', max_length=100, null=False, blank=False, db_comment='菜单名称')
    url = models.CharField(verbose_name='菜单URL', max_length=200, null=True, blank=True, db_comment='菜单URL')
    icon = models.CharField(verbose_name='菜单图标', max_length=50, null=True, blank=True, db_comment='菜单图标')
    menu_type = models.IntegerField(verbose_name='菜单类型', choices=((0, '目录'), (1, '菜单'), (2, '按钮')), default=1, db_comment='菜单类型')
    parent_id = TreeForeignKey('self', related_name='children', verbose_name='父菜单主键ID', on_delete=models.CASCADE, null=True, blank=True, db_column='parent_id', db_comment='父菜单主键ID')
    level_num = models.IntegerField(verbose_name='层级数', null=True, blank=True, db_comment='层级数')
    sort_num = models.IntegerField(verbose_name='排序号', null=True, blank=True, db_comment='排序号')
    platform_id = models.ForeignKey(Platform, to_field='platform_id', related_name='menus', verbose_name='所属平台', on_delete=models.CASCADE, null=True, blank=True, db_column='platform_id', db_comment='所属平台ID')
    def __str__(self):
        menu_type_dict = {0: '目录', 1: '菜单', 2: '按钮'}
        return f'{self.name}（{self.url}；{menu_type_dict.get(self.menu_type)}；{self.platform_id}）'

    class MPTTMeta:
        # 指定父节点字段名
        parent_attr = 'parent_id'
        # 指定使用 level_num 作为层级字段
        level_attr = 'level_num'
        # 按排序号和菜单ID排序
        order_insertion_by = ['sort_num', 'id']

    class Meta:
        db_table = 'menu'
        verbose_name = '菜单'
        verbose_name_plural = verbose_name


# 角色菜单关联
class RoleMenu(CommonInfo):
    role = models.ForeignKey('user.Role', related_name='role_menus', verbose_name='角色', on_delete=models.CASCADE, db_column='role_id', db_comment='角色ID')
    menu = models.ForeignKey(Menu, related_name='role_menus', verbose_name='菜单', on_delete=models.CASCADE, db_column='menu_id', db_comment='菜单ID')
    class Meta:
        db_table = 'role_menu'
        verbose_name = '角色菜单'
        verbose_name_plural = verbose_name
        constraints = [
            # 联合唯一索引，保证角色和菜单的唯一性
            models.UniqueConstraint(fields=['role', 'menu'], name='unique_role_menu')
        ]
    def __str__(self):
        return self.role.name + '-' + self.menu.name
